/*\
|*| chatabase JSON request 'messages' tests
|*|
|*| suite-specific test fixures data is defined in prepareTestFixures()
|*| the main difference between the 'client' tests and the 'messages' tests
|*|     is that the 'client' tests register a channel and require no authentication
|*|     but the 'messages' tests require a valid client secret
|*| upon registration, a client secret is returned
|*|     to be used in combination with the canonical 'ROOM' name as authentication
|*|     so if the 'client' tests were run previously
|*|     then there should be such a secret in the prim description
|*| the 'message' tests should be run prior to these so that the expected data exists
\*/


// suite-specific test data
string  SUITE_NAME     = "messages" ;
string  RESOURCE_PATH  = "/clients/{{ROOM_NAME}}/messages.json" ;
string  MESSAGE_1_TEXT = "This is the Message 1 Text" ;
string  MESSAGE_2_TEXT = "This is the Message 2 Text" ;
string  MESSAGE_3_TEXT = "This is the Message 3 Text" ;
list    FIXTURES ;   // prepareTestFixures()
integer N_FIXTURES ; // prepareTestFixures()

// runtime switches
integer DEBUG_EVS = FALSE ;

// shared project-specific constants
string  GRID_NAME ;          // loadSharedData()
string  REGION_NAME ;        // loadSharedData()
string  PARCEL_NAME ;        // loadSharedData()
string  ROOM_NAME_SENTINAL ; // loadSharedData()
string  EXCEPTION_RESP ;     // loadSharedData()
string  SECRET_RESP ;        // loadSharedData()
string  OWNER_NAME ;         // loadSharedData()
string  ROOM_NAME ;          // loadSharedData()
string  CLIENT_SECRET ;      // loadSharedData()
integer GRID_IDX      = -1 ; // ASSERT: negative int NOT DATA_LOAD_IDX
integer REGION_IDX    = -2 ; // ASSERT: negative int NOT DATA_LOAD_IDX
integer PARCEL_IDX    = -3 ; // ASSERT: negative int NOT DATA_LOAD_IDX
integer SENTINEL_IDX  = -4 ; // ASSERT: negative int NOT DATA_LOAD_IDX
integer EXCEPTION_IDX = -5 ; // ASSERT: negative int NOT DATA_LOAD_IDX
integer SECRET_IDX    = -6 ; // ASSERT: negative int NOT DATA_LOAD_IDX
integer OWNER_IDX     = -7 ; // ASSERT: negative int NOT DATA_LOAD_IDX

// link messages
integer DATA_LOAD_IDX   = -420 ; // ASSERT: negative int
key     DATA_RETURN_EVT = "data" ;
integer N_PARAMS        = 4 ;
integer DESCRIPTION_IDX = 0 ; // ASSERT: FIXTURES strided index
integer REQUEST_IDX     = 1 ; // ASSERT: FIXTURES strided index
integer RESPONSE_IDX    = 2 ; // ASSERT: FIXTURES strided index
integer STATUS_IDX      = 3 ; // ASSERT: FIXTURES strided index
integer ENDPOINT_IDX    = 4 ;

// http status
integer HTTP_STATUS_SUCCESS  = 200 ;
integer HTTP_STATUS_BAD      = 400 ;
integer HTTP_STATUS_REJECTED = 420 ;


// runtime vars
integer IsInitialized = FALSE ;


/* state default functions */

integer loadSharedData(integer data_idx , string data)
{
  if (data == "") return FALSE ;

  if      (data_idx == GRID_IDX     ) GRID_NAME          = data ;
  else if (data_idx == REGION_IDX   ) REGION_NAME        = data ;
  else if (data_idx == PARCEL_IDX   ) PARCEL_NAME        = data ;
  else if (data_idx == SENTINEL_IDX ) ROOM_NAME_SENTINAL = data ;
  else if (data_idx == EXCEPTION_IDX) EXCEPTION_RESP     = data ;
  else if (data_idx == SECRET_IDX   ) SECRET_RESP        = data ;
  else if (data_idx == OWNER_IDX    ) OWNER_NAME         = data ;

  integer is_ready = GRID_NAME      != "" && REGION_NAME        != "" &&
                     PARCEL_NAME    != "" && ROOM_NAME_SENTINAL != "" &&
                     EXCEPTION_RESP != "" && EXCEPTION_RESP     != "" &&
                     EXCEPTION_RESP != "" && OWNER_NAME         != ""  ;

  if (is_ready)
  {
    ROOM_NAME     = llDumpList2String([ GRID_NAME , REGION_NAME , PARCEL_NAME ] , " ") ;
    RESOURCE_PATH = strReplace(RESOURCE_PATH , ROOM_NAME_SENTINAL , ROOM_NAME) ;
    CLIENT_SECRET = llGetObjectDesc() ;
  }

  return is_ready ;
}

prepareTestFixures()
{
  /* client creation schema
  {
    client:
    {
      nick:   "FirstName LastName"               ,
      text:   "This is the Message Text"         ,
      secret: "d16c91602c5e190689e54b22165082bf"
    }
  }
  */


  /* fixture stride schema
  [
    test description       .
    outgoing request JSON  ,
    expected response JSON ,
    expected status
  ]
  */


  /* fixtures */

  FIXTURES =
  [
    "should not READ Messages with missing Client params"   ,
    "{}"                                                    ,
    EXCEPTION_RESP                                          ,
    HTTP_STATUS_BAD                                         ,

    "should not READ Messages with missing secret"          ,
    "{ 'client': { 'secret-is': 'the-only-param'       } }" ,
    EXCEPTION_RESP                                          ,
    HTTP_STATUS_BAD                                         ,

    "should not READ Messages with empty secret"            ,
    "{ 'client': { 'secret': ''                        } }" ,
    EXCEPTION_RESP                                          ,
    HTTP_STATUS_BAD                                         ,

    "should not READ Messages with wrong secret"            ,
    "{ 'client': { 'secret': '" + "CLIENT_SECRET" + "' } }" ,
    EXCEPTION_RESP                                          ,
    HTTP_STATUS_REJECTED                                    ,

    "should READ Messages with valid params"                ,
    "{ 'client': { 'secret': '" +  CLIENT_SECRET  + "' } }" ,
    MESSAGE_2_TEXT                                          ,
    HTTP_STATUS_SUCCESS
  ] ;


  /* validation */

  if (llGetListLength(FIXTURES) % N_PARAMS) FIXTURES = [] ;
  N_FIXTURES = llGetListLength(FIXTURES) / N_PARAMS ;
}


/* helpers */

string strReplace(string str , string sentinel , string replace)
{
  return llDumpList2String(llParseStringKeepNulls(str , [sentinel] , []) , replace) ;
}

DBG_EVS(string msg) { if (DEBUG_EVS) llOwnerSay(msg) ; }

sendLinkMsg(integer data_idx , string data)
{
DBG_EVS("CLIENT::SEND    -> " + llList2CSV([DATA_RETURN_EVT , data_idx , data])) ;

  llMessageLinked(LINK_THIS , data_idx , data , DATA_RETURN_EVT) ;
}


default
{
  state_entry() { IsInitialized = FALSE ; }

  link_message(integer sender , integer data_idx , string data , key evt)
  {
    if (evt != SUITE_NAME) return ;

DBG_EVS("CLIENT::RECV    <- " + llList2CSV([evt , data_idx , data])) ;

    if (!loadSharedData(data_idx , data)) return ;

    IsInitialized = TRUE ; prepareTestFixures() ; state ready ;
  }
}

state ready
{
  state_entry() { sendLinkMsg(DATA_LOAD_IDX , (string)N_FIXTURES) ; }

  link_message(integer sender , integer test_n , string unused , key evt)
  {
    if (evt != SUITE_NAME) return ;

DBG_EVS("CLIENT::RECV    <- " + llList2CSV([evt , test_n , unused])) ;

    integer is_valid_test_n = test_n >= 0 && test_n < N_FIXTURES ;
    integer offset_idx      = N_PARAMS * test_n ;

    if (is_valid_test_n)
    {
      string  request_json  = llList2String (FIXTURES , offset_idx + REQUEST_IDX    ) ;
      string  response_json = llList2String (FIXTURES , offset_idx + RESPONSE_IDX   ) ;
      integer status        = llList2Integer(FIXTURES , offset_idx + STATUS_IDX     ) ;
      string  description   = llList2String (FIXTURES , offset_idx + DESCRIPTION_IDX) ;

      sendLinkMsg(REQUEST_IDX     , request_json  ) ;
      sendLinkMsg(RESPONSE_IDX    , response_json ) ;
      sendLinkMsg(STATUS_IDX      , (string)status) ;
      sendLinkMsg(DESCRIPTION_IDX , description   ) ;
      sendLinkMsg(ENDPOINT_IDX    , RESOURCE_PATH ) ;
    }
    else state default ;
  }
}
